package com.tinywind.boot.module.system.controller;

import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpUtil;
import com.tinywind.boot.common.annotation.LogAround;
import com.tinywind.boot.common.base.BaseResult;
import com.tinywind.boot.common.consts.SystemConst;
import com.tinywind.boot.common.utils.RedisUtil;
import com.tinywind.boot.common.utils.encrypt.SHA256Utils;
import com.tinywind.boot.module.system.entity.SysUserInfo;
import com.tinywind.boot.module.system.service.LoginService;
import com.tinywind.boot.module.system.vo.MenuVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.*;

/**
 * @author liuxingyu01
 * @date 2021-12-22-20:46
 * @description 系统登录控制器
 **/
@Api(value = "LoginController", description = "系统登陆")
@Controller
@RequestMapping("/system")
public class LoginController {
    final static Logger logger = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    private LoginService loginService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private StpInterface stpInterface;

    /**
     * 加密盐
     */
    @Value("${hash.salt}")
    private String salt;

    /**
     * token名称 (同时也是cookie名称)
     */
    @Value("${sa-token.token-name}")
    private String tokenName;

    @LogAround(value = "执行系统登陆操作")
    @ApiOperation(value = "执行系统登陆操作", notes = "执行系统登陆操作")
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public BaseResult login(@RequestParam("username") String username,
                            @RequestParam("password") String password,
                            @RequestParam("code") String code,
                            @RequestParam("uuid") String uuid) {
        String verifyKey = SystemConst.CAPTCHA_CODE_KEY + ":" + uuid;
        String captcha = (String) redisUtil.get(verifyKey);

        if (StringUtils.isNotBlank(captcha) && captcha.equalsIgnoreCase(code)) {
            // 验证成功，删除点缓存
            redisUtil.del(verifyKey);
        } else {
            return BaseResult.failure("验证码错误或已过期，请重新输入!");
        }

        SysUserInfo userInfo = loginService.getUserInfo(username);

        // 没找到帐号(用户不存在)
        if (userInfo == null) {
            return BaseResult.failure("账户不存在！");
        }
        // 校验用户状态(用户已失效)
        if ("1".equals(userInfo.getStatus())) {
            return BaseResult.failure("该账户已被冻结！");
        }
        // 密码错误五次，则锁定账号1800s
        int errorTimes = redisUtil.get(SystemConst.SYSTEM_LOGIN_TIMES + ":" + userInfo.getAccount()) == null ? 0
                : Integer.parseInt((String) redisUtil.get(SystemConst.SYSTEM_LOGIN_TIMES + ":" + userInfo.getAccount()));
        if (errorTimes >= 5) {
            redisUtil.expire(SystemConst.SYSTEM_LOGIN_TIMES + ":" + username, 1800);
            return BaseResult.failure("密码连续输入错误超过5次，账号将被锁定半小时！");
        }

        // 数据库里存的密码
        String localPassword = userInfo.getPassword();
        // 前端传来的代码进行加密
        password = SHA256Utils.SHA256Encode(salt + password);
        // 对比
        if (localPassword.equals(password)) {
            logger.info("LoginController - doLogin - {}登陆成功！", username);

            // 执行登录
            StpUtil.login(userInfo.getUserId());
            // 缓存用户会话信息
            StpUtil.getTokenSession().set(SystemConst.SYSTEM_USER_INFO, userInfo);

            Map<String, Object> resultMap = new HashMap<>();
            resultMap.put(tokenName, StpUtil.getTokenValue());
            resultMap.put("user", username);

            return BaseResult.success("登录成功，欢迎回来！", resultMap);
        } else {
            recordLoginTimes(username);
            return BaseResult.failure("密码错误，请重新输入！");
        }
    }


    /**
     * 当密码错误时，做记录
     *
     * @param username
     */
    private void recordLoginTimes(String username) {
        // 获取当前用户的密码错误次数
        int errorTimes = redisUtil.get(SystemConst.SYSTEM_LOGIN_TIMES + ":" + username) == null ? 0
                : Integer.parseInt((String) redisUtil.get(SystemConst.SYSTEM_LOGIN_TIMES + ":" + username));
        redisUtil.set(SystemConst.SYSTEM_LOGIN_TIMES + ":" + username, String.valueOf(errorTimes + 1), 1800);
    }


    @LogAround(value = "执行初始化系统信息操作")
    @ApiOperation(value = "执行初始化系统信息操作", notes = "执行初始化系统信息操作")
    @RequestMapping(value = "/getInfo", method = RequestMethod.GET)
    @ResponseBody
    public BaseResult getInfo() {
        SysUserInfo userInfo =  StpUtil.getTokenSession().getModel(SystemConst.SYSTEM_USER_INFO, SysUserInfo.class);
        userInfo.setPassword(null);

        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("username", userInfo.getAccount());

        // 获取用户角色信息
        List<String> roleSet = stpInterface.getRoleList(StpUtil.getLoginId(), StpUtil.getLoginType());
        // 获取用户权限列表
        List<String> permissionSet = stpInterface.getPermissionList(StpUtil.getLoginId(), StpUtil.getLoginType());
        // 组织菜单列表
        List<MenuVo> menuList = loginService.listMenuByUserId(userInfo.getUserId());

        resultMap.put("permissions", permissionSet);
        resultMap.put("roles", roleSet);
        resultMap.put("menus", menuList);
        resultMap.put("userinfo", userInfo);

        return BaseResult.success("获取菜单信息成功！", resultMap);
    }


    @LogAround(value = "执行退出登陆操作")
    @ApiOperation(value = "执行退出登陆操作", notes = "执行退出登陆操作")
    @RequestMapping(value = "/logout", method = RequestMethod.GET)
    @ResponseBody
    public BaseResult logout() {
        StpUtil.logout();
        return BaseResult.success("退出登录成功！", null);
    }

}
